﻿// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com

// ReSharper disable CheckNamespace
// ReSharper disable ClassNeverInstantiated.Global
// ReSharper disable CommentTypo
// ReSharper disable IdentifierTypo
// ReSharper disable InconsistentNaming
// ReSharper disable StringLiteralTypo
// ReSharper disable UnusedParameter.Local

/* CountdownMemoryOwner.cs --
 * Ars Magna project, http://arsmagna.ru
 */

#region Using directives

using System;
using System.Buffers;
using System.Runtime.CompilerServices;

#endregion

#nullable enable

namespace AM.Memory;

/// <summary>
///     Encapsulates manual memory management mechanism. Holds
///     IMemoryOwner instance goes to GC (link = null) only when
///     all owning entities called Dispose() method. This means,
///     that only this mechanism should be used for covering
///     managed instances.
/// </summary>
public sealed class CountdownMemoryOwner<T>
    : IMemoryOwner<T>
{
    #region Properties

    /// <summary>
    /// Владеемая память.
    /// </summary>
    public Memory<T> Memory
    {
        [MethodImpl (MethodImplOptions.AggressiveInlining)]
        get;
        private set;
    }

    #endregion

    #region Private members

    private int _length;
    private int _offset;
    private int _owners;
    private T[]? _arr;
    private CountdownMemoryOwner<T>? _parent;


    [MethodImpl (MethodImplOptions.AggressiveInlining)]
    internal CountdownMemoryOwner<T> Init
        (
            CountdownMemoryOwner<T> parent,
            int offset,
            int length,
            bool defaultOwner = true
        )
    {
        _owners = defaultOwner ? 1 : 0;
        _offset = offset;
        _length = length;
        _parent = parent;
        _parent.AddOwner();
        Memory = _parent.Memory.Slice (_offset, _length);
        return this;
    }

    [MethodImpl (MethodImplOptions.AggressiveInlining)]
    internal CountdownMemoryOwner<T> Init
        (
            T[] array,
            int length
        )
    {
        _owners = 1;
        _offset = 0;
        _length = length;
        _parent = default;
        _arr = array;
        Memory = _arr.AsMemory (0, _length);

        return this;
    }

    #endregion

    #region Public methods

    /// <summary>
    /// Ручное добавление владельца.
    /// </summary>
    [MethodImpl (MethodImplOptions.AggressiveInlining)]
    public void AddOwner() => _owners++;

    #endregion

    #region IDisposable members

    /// <inheritdoc cref="IDisposable.Dispose"/>
    [MethodImpl (MethodImplOptions.AggressiveInlining)]
    public void Dispose()
    {
        _owners--;
        if (_owners > 0)
        {
            return;
        }

        if (_parent != default)
        {
            _parent.Dispose();
            _parent = default;
        }
        else
        {
            ArrayPool<T>.Shared.Return (_arr!);
        }

        Pool<CountdownMemoryOwner<T>>.Return (this);
    }

    #endregion
}
